package main

import (
	"bytes"
	"fmt"
	"runtime"
	"strconv"
	"sync"
)

func main() {
	// foo1()
	foo2()
}

// func Trace() func() {
// 	pc, _, _, ok := runtime.Caller(1)
// 	if !ok {
// 		return func() {}
// 	}
// 	fn := runtime.FuncForPC(pc)
// 	name := fn.Name()
// 	println("=> enter ", name)
// 	return func() {
// 		println("=> exit ", name)
// 	}
// }

// 获取goroutineID
var goroutineSpace = []byte("goroutine ")

func curGoroutineID() uint64 {
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	// Parse the 4707 out of "goroutine 4707 ["
	b = bytes.TrimPrefix(b, goroutineSpace)
	i := bytes.IndexByte(b, ' ')
	if i < 0 {
		panic(fmt.Sprintf("No space found in %q", b))
	}
	b = b[:i]
	n, err := strconv.ParseUint(string(b), 10, 64)
	if err != nil {
		panic(fmt.Sprintf("Failed to parse goroutine ID out of %q: %v", b, err))
	}
	return n
}

func Trace() func() {
	pc, _, _, ok := runtime.Caller(1)
	if !ok {
		return func() {}
	}
	fn := runtime.FuncForPC(pc)
	name := fn.Name()
	gid := curGoroutineID()
	fmt.Printf("=>[g%05d]:enter %s\n", gid, name)
	return func() {
		fmt.Printf("<=[g%05d]:exit %s\n", gid, name)
	}
}

func foo1() {
	A()
	C()
	B()
}

func A() {
	defer Trace()()
}

func B() {
	defer Trace()()
}

func C() {
	defer Trace()()
}

func foo2() {
	wg := sync.WaitGroup{}

	wg.Add(1)
	go func() {
		foo1()
		wg.Done()
	}()
	B()
	A()
	C()
	wg.Wait()
}
